package top.zsyle.basic.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import top.zsyle.basic.config.BussinessException;
import top.zsyle.basic.constant.LoginConst;
import top.zsyle.basic.constant.RegisterConst;
import top.zsyle.basic.dto.EmailCodeDto;
import top.zsyle.basic.dto.LoginDto;
import top.zsyle.basic.dto.SmsCodeDto;
import top.zsyle.basic.service.VerifyCodeService;
import top.zsyle.basic.util.EmailUtil;
import top.zsyle.basic.util.StrUtils;
import top.zsyle.basic.util.VerifyCodeUtils;
import top.zsyle.user.domain.Logininfo;
import top.zsyle.user.domain.User;
import top.zsyle.user.mapper.LogininfoMapper;
import top.zsyle.user.mapper.UserMapper;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class VerifyCodeServiceImpl implements VerifyCodeService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogininfoMapper logininfoMapper;

    /**
     * 获取图形验证码的base64
     * @param imageCodeKey
     * @return
     */
    @Override
    public String graph(String imageCodeKey) {
        //1、随机生成图形验证码字符串
        String imageCode = StrUtils.getComplexRandomString(RegisterConst.IMAGE_CODE_LENGTH);
        //2、将随机字符串设置到redis中
        redisTemplate.opsForValue().set(imageCodeKey,imageCode, RegisterConst.IMAGE_CODE_TTL, TimeUnit.MINUTES);

        //3、将字符串生成图形验证码base64
        String base64Str = VerifyCodeUtils.VerifyCode(RegisterConst.IMAGE_CODE_WIDTH, RegisterConst.IMAGE_CODE_HIGHT, imageCode);
        return base64Str;
    }

    /**
     * 获取短信验证码
     * @param dto
     */
    @Override
    public void smsCode(SmsCodeDto dto) {
        if("binder".equals(dto.getType())){
            verifySmsCode(dto, LoginConst.BINDER_PHONE_KEY);
        }else if ("register".equals(dto.getType())){
            //1.校验参数
            imageVerify(dto);
            //4.发送手机验证码事件间隔必须60秒以上
            verifySmsCode(dto,RegisterConst.REGISTER_PHONE_KEY);
        }

    }

    /**
     * 图片验证
     * @param dto
     */
    private void imageVerify(SmsCodeDto dto) {
        //校验图片验证码
        if (StringUtils.isBlank(dto.getImageCodeKey())){
            throw new BussinessException("ImageCodeKey不能为空");
        }
        if (StringUtils.isBlank(dto.getImageCodeValue())){
            throw new BussinessException("图片验证码不能为空");
        }
        User user = userMapper.findByPhone(dto.getPhone());
        if (null != user){
            throw new BussinessException("手机已经注册，快去登录吧!");
        }
        //3.校验图片验证码是否正确
        //获取redis中的图片验证码
        String imageCodeRedis = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey());
        if (null==imageCodeRedis || !imageCodeRedis.equalsIgnoreCase(dto.getImageCodeValue())){
            throw new BussinessException("图片验证码不正确！");
        }
    }



    /**
     * 获取 邮箱验证码
     * @param dto
     */
    @Override
    public void emailCode(EmailCodeDto dto) {
        //1.校验参数
        //2.检验邮箱是否已经注册
        User user = userMapper.findByEmail(dto.getEmail());
        if (null != user){
            throw new BussinessException("邮箱该已经注册了");
        }
        //3.校验图片验证码是否正确
        String imageCodeRedis = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey());
        if (null == imageCodeRedis || !imageCodeRedis.equalsIgnoreCase(dto.getImageCodeValue())){
            throw new BussinessException("图片验证码不正确");
        }
        //4.发送邮箱验证码事件间隔必须60秒以上
        Long ttl = redisTemplate.getExpire(String.format(RegisterConst.REGISTER_EMAIL_KEY,dto.getEmail()));
        if (null != ttl && ttl > ((RegisterConst.EMAIL_CODE_TTL-1)*60)){
            throw new BussinessException("发送邮件间隔必须在60秒以上");
        }
        //5.随机生成邮箱验证码并保存到redis中
        String emailCode = StrUtils.getRandomString(RegisterConst.EMAIL_CODE_LENGTH);
        redisTemplate.opsForValue().set(String.format(RegisterConst.REGISTER_EMAIL_KEY,dto.getEmail()),emailCode);
        //6.发送短信验证码
        EmailUtil.sendEmailCode(emailCode);
    }

    /**
     * 短信验证码登录
     * @param dto
     */
    @Override
    public void smsCodeLogin(SmsCodeDto dto) {
        //1.校验参数
        //2.检验电话是否已经注册
        LoginDto loginDto = new LoginDto();
        loginDto.setAccount(dto.getPhone());
        loginDto.setType(1);
        Logininfo logininfo = logininfoMapper.findByDto(loginDto);
        if (null == logininfo){
            throw new BussinessException("你还未注册，快去注册吧");
        }
        //3.校验图片验证码是否正确
        //获取redis中的图片验证码
        String imageCodeRedis = (String) redisTemplate.opsForValue().get(dto.getImageCodeKey());
        if (null==imageCodeRedis || !imageCodeRedis.equalsIgnoreCase(dto.getImageCodeValue())){
            throw new BussinessException("图片验证码不正确！");
        }
        //4.发送手机验证码事件间隔必须60秒以上
        verifySmsCode(dto,RegisterConst.REGISTER_PHONE_KEY);
    }

    /**
     * 校验短信验证码：
     * 间隔时间必须超过60s、检验验证码是否过期、并保存到redis中
     * 发送短信验证码
     * @param dto
     * @param redisKey
     */
    private void verifySmsCode(SmsCodeDto dto,String redisKey) {
        Long ttl = redisTemplate.getExpire(redisKey+ dto.getPhone());
        if (null != ttl && ttl > ((RegisterConst.PHONE_CODE_TTL-1)*60)){
            //间隔没有60
            throw new BussinessException("你获取的太频繁了，请60秒后再获取！");
        }
        //5.检查手机验证码是否过期，若没过期，则发送与上一次验证码相同的短信，并重新设置过期时间
        String smsCode = "";
        String redisSmsCode = (String) redisTemplate.opsForValue().get(redisKey + dto.getPhone());
        if (StringUtils.isNotBlank(redisSmsCode)){
            //未过期
            smsCode = redisSmsCode;
        }else {
            //短信验证码已过期 新生成一个短信验证码 并保存到redis中
            smsCode = StrUtils.getRandomString(RegisterConst.PHONE_CODE_LENGTH);
        }
        //6.将短信保存到redis中
        redisTemplate.opsForValue().set(redisKey+ dto.getPhone(), smsCode,RegisterConst.PHONE_CODE_TTL, TimeUnit.MINUTES);
        //7.发送短信验证码
//        SendMsgUtil.sendMsg(dto.getPhone(),"您好，你的验证码为:"+smsCode+"3分钟内有效");
        log.info("短信验证码：{}",smsCode);
    }
}
